﻿using System;
using System.Net;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Linq;

namespace Airport
{
    [DataContract]
    [KnownType(typeof(RouteNode))]
    public class Route : IRoute, IEquatable<Route>
    {
        private int _routeId;
        private List<RouteNode> _routeNodes;
        private List<Edge> _routeEdges;
        private List<Taxiway> _taxiways;
        private List<Runway> _runways;
        private Point _startPoint;
        private DateTime _startTime;
        private DateTime _endTime;
        private Plane _routePlane;
        private string _routeStrings;

        /// <summary>
        /// Empty <c>Route</c> constructor.
        /// </summary>
        public Route()
            : this(-1)
        {
        }

        /// <summary>
        /// Creates a new <c>Route</c> with the given ID.
        /// </summary>
        /// <param name="routeId">int representing the explicit route ID for this <c>Route</c>.</param>
        public Route(int routeId)
        {
            _routeId = routeId;
            _routeNodes = new List<RouteNode>();
            _routeEdges = new List<Edge>();
            _taxiways = new List<Taxiway>();
            _runways = new List<Runway>();
            _startPoint = new Point();
            _startTime = new DateTime();
            _endTime = new DateTime();
            _routePlane = null;
            _routeStrings = "";
        }

        // Copy Constructor
        public Route(Route otherRoute)
        {
            _routeId = otherRoute._routeId;
            _routeNodes = new List<RouteNode>(otherRoute._routeNodes);
            _routeEdges = new List<Edge>(otherRoute._routeEdges);
            //_taxiways = new List<Taxiway>(otherRoute._taxiways);
            //_runways = new List<Runway>(otherRoute._runways);
            _startPoint = otherRoute._startPoint;
            _startTime = otherRoute._startTime;
            _endTime = otherRoute._endTime;
            _routePlane = otherRoute._routePlane;
            _routeStrings = otherRoute._routeStrings;
        }

        [DataMember]
        public int RouteId
        {
            get
            {
                return _routeId;
            }

            set
            {
                if (_routeId != value)
                {
                    _routeId = value;
                }
            }
        }

        [DataMember]
        public List<RouteNode> RouteNodes
        {
            get
            {
                return _routeNodes;
            }

            set
            {
                if (_routeNodes != value)
                {
                    _routeNodes = value;

                    if (_routeNodes != null && _routeNodes.Count > 0)
                    {
                        // Set start point to the first node in the Route
                        _startPoint = new Point(_routeNodes[0].XPos, _routeNodes[0].YPos);

                        // Set start time to PredictedTOA of first RouteNode
                        _startTime = _routeNodes[0].PredictedTOA;

                        // Set end time to PredictedTOA of last RouteNode
                        _endTime = _routeNodes[_routeNodes.Count - 1].PredictedTOA;
                    }
                    else
                    {
                        _startPoint = null;
                        _startTime = new DateTime();
                        _endTime = new DateTime();
                    }
                }
            }
        }

        [DataMember]
        public List<Edge> RouteEdges
        {
            get
            {
                return _routeEdges;
            }

            set
            {
                _routeEdges = value;

                // Set _routeStrings with the edge names
                if (_routeEdges != null)
                {
                    List<string> edgeNames = (from edge in _routeEdges
                                              select edge.Name).Distinct().ToList();
                    string edgeNamesString = "";

                    // Construct a string the edge names
                    if (edgeNames.Count > 0)
                    {
                        for (int i = 0; i < edgeNames.Count - 1; i++)
                        {
                            edgeNamesString += edgeNames[i] + ", ";
                        }
                        edgeNamesString += edgeNames[edgeNames.Count - 1];
                    }
                    _routeStrings = edgeNamesString;
                }
                else
                {
                    _routeStrings = "";
                }
            }
        }

        //[DataMember]
        public List<KeyValuePair<RouteNode, Edge>> RouteNodeEdgePairs
        {
            get
            {
                List<KeyValuePair<RouteNode, Edge>> nodeEdgePairs = new List<KeyValuePair<RouteNode, Edge>>();

                if (_routeNodes != null && _routeNodes.Count > 0)
                {
                    for (int i = 0; i < _routeEdges.Count; i++)
                    {
                        nodeEdgePairs.Add(new KeyValuePair<RouteNode, Edge>(_routeNodes[i], _routeEdges[i]));
                    }
                    nodeEdgePairs.Add(new KeyValuePair<RouteNode, Edge>(_routeNodes[_routeNodes.Count - 1], null));
                }
                return nodeEdgePairs;
            }

            set
            {
                List<RouteNode> newRouteNodes = new List<RouteNode>();
                List<Edge> newRouteEdges = new List<Edge>();

                if (value != null)
                {
                    foreach (KeyValuePair<RouteNode, Edge> nodeEdgePair in value)
                    {
                        newRouteNodes.Add(nodeEdgePair.Key);

                        if (nodeEdgePair.Value != null)
                        {
                            newRouteEdges.Add(nodeEdgePair.Value);
                        }
                    }
                }
                RouteNodes = newRouteNodes;
                RouteEdges = newRouteEdges;
            }
        }

        //[DataMember]
        public List<KeyValuePair<Node, Edge>> NodeEdgePairs
        {
            get
            {
                List<KeyValuePair<Node, Edge>> nodeEdgePairs = new List<KeyValuePair<Node, Edge>>();

                if (_routeNodes != null && _routeNodes.Count > 0)
                {
                    for (int i = 0; i < _routeEdges.Count; i++)
                    {
                        nodeEdgePairs.Add(new KeyValuePair<Node, Edge>(_routeNodes[i], _routeEdges[i]));
                    }
                    nodeEdgePairs.Add(new KeyValuePair<Node, Edge>(_routeNodes[_routeNodes.Count - 1], null));
                }
                return nodeEdgePairs;
            }
        }

        //[DataMember]
        public List<Taxiway> Taxiways
        {
            get
            {
                return _taxiways;
            }

            set
            {
                if (_taxiways != value)
                {
                    _taxiways = value;
                }
            }
        }

        //[DataMember]
        public List<Runway> Runways
        {
            get
            {
                return _runways;
            }

            set
            {
                if (_runways != value)
                {
                    _runways = value;
                }
            }
        }

        [DataMember]
        public Point StartPoint
        {
            get
            {
                return _startPoint;
            }

            set
            {
                _startPoint = value;
            }
        }

        [DataMember]
        public DateTime StartTime
        {
            get
            {
                return _startTime;
            }

            set
            {
                _startTime = value;
            }
        }

        [DataMember]
        public DateTime EndTime
        {
            get
            {
                return _endTime;
            }

            set
            {
                _endTime = value;
            }
        }

        [DataMember]
        public Plane RoutePlane
        {
            get
            {
                return _routePlane;
            }

            set
            {
                _routePlane = value;
            }
        }

        [DataMember]
        public string RouteStrings
        {
            get
            {
                return _routeStrings;
            }

            set
            {
                _routeStrings = value;
            }
        }

        public bool Equals(Route other)
        {
            if (other == null)
            {
                return false;
            }

            // Check that both routes have the same number of RouteNodes
            if (_routeNodes.Count != other._routeNodes.Count)
            {
                return false;
            }
            /*
            // Check that both routes have the same number of Taxiways
            if (_taxiways.Count != other._taxiways.Count)
            {
                return false;
            }

            // Check that both routes have the same number of Runways
            if (_runways.Count != other._runways.Count)
            {
                return false;
            }
            */
            // Check that all RouteNodes are in both Routes
            foreach (RouteNode routeNode in other._routeNodes)
            {
                if (!_routeNodes.Contains(routeNode))
                {
                    return false;
                }
            }
            /*
            // Check that all Taxiways are in both Routes
            foreach (Taxiway taxiway in other._taxiways)
            {
                if (!_taxiways.Contains(taxiway))
                {
                    return false;
                }
            }

            // Check that all Runways are in both Routes
            foreach (Runway runway in other._runways)
            {
                if (!_runways.Contains(runway))
                {
                    return false;
                }
            }
            */
            return true;
        }

        public override string ToString()
        {
            string routeNodesString = "";

            foreach (RouteNode node in _routeNodes)
            {
                routeNodesString += node;
            }

            return routeNodesString;
        }
    }

    
}
